﻿using System;

namespace KWatkins.FluentMoqBuilder.Argument
{
	/// <summary>
	/// Validates a signed, imprecise numeric argument.
	/// </summary>
	/// <typeparam name="T">The numeric type.</typeparam>
	internal interface ISignedImpreciseNumber<T>
		where T : struct
	{
		/// <summary>
		/// Validates the argument is greater than some value.
		/// </summary>
		/// <param name="minimum">The minimum value to test against.</param>
		/// <exception cref="ArgumentOutOfRangeException">
		/// If the argument is not greater than <paramref name="minimum" />.
		/// </exception>
		/// <returns>A <see cref="ISignedImpreciseNumber{T}" /> to continue validation.</returns>
		ISignedImpreciseNumber<T> IsGreaterThan(T minimum);

		/// <summary>
		/// Validates the argument is greater than some value within a specified absolute
		/// tolerance.
		/// </summary>
		/// <param name="minimum">The minimum value to test against.</param>
		/// <param name="tolerance">The absolute tolerance to use.</param>
		/// <exception cref="ArgumentOutOfRangeException">
		/// If the argument is not greater than <paramref name="minimum" /> - <paramref name="tolerance" />.
		/// </exception>
		/// <returns>A <see cref="ISignedImpreciseNumber{T}" /> to continue validation.</returns>
		ISignedImpreciseNumber<T> IsGreaterThan(T minimum, T tolerance);

		/// <summary>
		/// Validates the argument is greater than or equal to some value.
		/// </summary>
		/// <param name="minimum">The minimum value to test against.</param>
		/// <exception cref="ArgumentOutOfRangeException">
		/// If the argument is not greater than or equal to <paramref name="minimum" />.
		/// </exception>
		/// <returns>A <see cref="ISignedImpreciseNumber{T}" /> to continue validation.</returns>
		ISignedImpreciseNumber<T> IsGreaterThanOrEqualTo(T minimum);

		/// <summary>
		/// Validates the argument is greater than or equal to some value within a specified
		/// absolute tolerance.
		/// </summary>
		/// <param name="minimum">The minimum value to test against.</param>
		/// <param name="tolerance">The absolute tolerance to use.</param>
		/// <exception cref="ArgumentOutOfRangeException">
		/// If the argument is not greater than or equal to <paramref name="minimum" /> - <paramref name="tolerance" />.
		/// </exception>
		/// <returns>A <see cref="ISignedImpreciseNumber{T}" /> to continue validation.</returns>
		ISignedImpreciseNumber<T> IsGreaterThanOrEqualTo(T minimum, T tolerance);

		/// <summary>
		/// Validates the argument is less than some value.
		/// </summary>
		/// <param name="maximum">The maximum value to test against.</param>
		/// <exception cref="ArgumentOutOfRangeException">
		/// If the argument is not less than <paramref name="maximum" />.
		/// </exception>
		/// <returns>A <see cref="ISignedImpreciseNumber{T}" /> to continue validation.</returns>
		ISignedImpreciseNumber<T> IsLessThan(T maximum);

		/// <summary>
		/// Validates the argument is less than some value within a specified
		/// absolute tolerance.
		/// </summary>
		/// <param name="maximum">The maximum value to test against.</param>
		/// <param name="tolerance">The absolute tolerance to use.</param>
		/// <exception cref="ArgumentOutOfRangeException">
		/// If the argument is not less than <paramref name="maximum" /> + <paramref name="tolerance" />.
		/// </exception>
		/// <returns>A <see cref="ISignedImpreciseNumber{T}" /> to continue validation.</returns>
		ISignedImpreciseNumber<T> IsLessThan(T maximum, T tolerance);

		/// <summary>
		/// Validates the argument is less than or equal to some value.
		/// </summary>
		/// <param name="maximum">The maximum value to test against.</param>
		/// <exception cref="ArgumentOutOfRangeException">
		/// If the argument is not less than or equal to <paramref name="maximum" />.
		/// </exception>
		/// <returns>A <see cref="ISignedImpreciseNumber{T}" /> to continue validation.</returns>
		ISignedImpreciseNumber<T> IsLessThanOrEqualTo(T maximum);

		/// <summary>
		/// Validates the argument is less than or equal to some value within a specified
		/// absolute tolerance.
		/// </summary>
		/// <param name="maximum">The maximum value to test against.</param>
		/// <param name="tolerance">The absolute tolerance to use.</param>
		/// <exception cref="ArgumentOutOfRangeException">
		/// If the argument is not less than or equal to <paramref name="maximum" /> + <paramref name="tolerance" />.
		/// </exception>
		/// <returns>A <see cref="ISignedImpreciseNumber{T}" /> to continue validation.</returns>
		ISignedImpreciseNumber<T> IsLessThanOrEqualTo(T maximum, T tolerance);

		/// <summary>
		/// Validates the argument is positive, i.e. greater than zero.
		/// </summary>
		/// <exception cref="ArgumentOutOfRangeException">
		/// If the argument is not positive.
		/// </exception>
		/// <returns>A <see cref="IUnsignedImpreciseNumber{T}" /> to continue validation.</returns>
		IUnsignedImpreciseNumber<T> IsPositive();

		/// <summary>
		/// Validates the argument is positive, i.e. greater than zero, within a specified
		/// absolute tolerance.
		/// </summary>
		/// <param name="tolerance">The absolute tolerance to use.</param>
		/// <exception cref="ArgumentOutOfRangeException">
		/// If the argument is less than or equal to 0 - <paramref name="tolerance"/>.
		/// </exception>
		/// <returns>A <see cref="IUnsignedImpreciseNumber{T}" /> to continue validation.</returns>
		IUnsignedImpreciseNumber<T> IsPositive(T tolerance);

        /// <summary>
		/// Validates the argument is not positive, i.e. negative or zero.
		/// </summary>
		/// <exception cref="ArgumentOutOfRangeException">
		/// If the argument is positive.
		/// </exception>
		/// <returns>A <see cref="IUnsignedImpreciseNumber{T}" /> to continue validation.</returns>
		IUnsignedImpreciseNumber<T> IsNotPositive();

		/// <summary>
		/// Validates the argument is not positive, i.e. negative or zero, within a specified
		/// absolute tolerance.
		/// </summary>
		/// <param name="tolerance">The absolute tolerance to use.</param>
		/// <exception cref="ArgumentOutOfRangeException">
		/// If the argument is greater than 0 + <paramref name="tolerance"/>.
		/// </exception>
		/// <returns>A <see cref="IUnsignedImpreciseNumber{T}" /> to continue validation.</returns>
		IUnsignedImpreciseNumber<T> IsNotPositive(T tolerance);

		/// <summary>
		/// Validates the argument is negative, i.e. less than zero.
		/// </summary>
		/// <exception cref="ArgumentOutOfRangeException">
		/// If the argument is not negative.
		/// </exception>
		/// <returns>A <see cref="IUnsignedImpreciseNumber{T}" /> to continue validation.</returns>
		IUnsignedImpreciseNumber<T> IsNegative();

		/// <summary>
		/// Validates the argument is negative, i.e. less than zero, within a specified
		/// absolute tolerance.
		/// </summary>
		/// <param name="tolerance">The absolute tolerance to use.</param>
		/// <exception cref="ArgumentOutOfRangeException">
		/// If the argument is greater than or equal to 0 + <paramref name="tolerance"/>.
		/// </exception>
		/// <returns>A <see cref="IUnsignedImpreciseNumber{T}" /> to continue validation.</returns>
		IUnsignedImpreciseNumber<T> IsNegative(T tolerance);

		/// <summary>
		/// Validates the argument is not negative, i.e. positive or zero.
		/// </summary>
		/// <exception cref="ArgumentOutOfRangeException">
		/// If the argument is not negative.
		/// </exception>
		/// <returns>A <see cref="IUnsignedImpreciseNumber{T}" /> to continue validation.</returns>
		IUnsignedImpreciseNumber<T> IsNotNegative();

		/// <summary>
		/// Validates the argument is not negative, i.e. positive or zero, within a specified
		/// absolute tolerance.
		/// </summary>
		/// <param name="tolerance">The absolute tolerance to use.</param>
		/// <exception cref="ArgumentOutOfRangeException">
		/// If the argument is less than or equal to 0 - <paramref name="tolerance"/>.
		/// </exception>
		/// <returns>A <see cref="IUnsignedImpreciseNumber{T}" /> to continue validation.</returns>
		IUnsignedImpreciseNumber<T> IsNotNegative(T tolerance);
	}
}